/*
Project:	CoreEngine
Author:		Dino Bojadzhievski
File:		Transform3Manager

Defines an abstraction layer over linear and affine transformations in three-dimensional space.

All performed with homogeneous coordinates (4D vectors and matrices).
All angles expressed in degrees.
Positive angles are counter-clockwise.

Abstracts away the mathematical limitations to performing linear and affine transformations in 3D space.
Achieves this by translating the vector to the origin, performing transforms, and translating back.
Allows for transform chaining without manual management of transform order.

Usage:
1. Instantiate a Transform3Manager object.
2. Queue transforms you need executed.
3. Call Perform on the instance, and assign the final computed value to the vector to be transformed.
*/

#pragma once

#include "../../CoreTypes.h"

#include "../Matrix2.h"
#include "../Matrix3.h"
#include "../Matrix4.h"
#include "../Vector3.h"
#include "../Vector4.h"

#include <stack>

#include "../Transform3D.h"

using namespace std;
using namespace CoreMath;

class Transform3Manager
{
private:
	stack<Matrix4> stk_TransformQueue;

public:

	void Init()
	{
		while (!this->stk_TransformQueue.empty())
			this->stk_TransformQueue.pop();
	}

	void QueueScale(const real factorX, const real factorY, const real factorZ)
	{
		Matrix4 m_TransformMatrix = GetScalingMatrix(factorX, factorY, factorZ);
		this->stk_TransformQueue.push(m_TransformMatrix);
	}

	void QueueTranslate(const real dX, const real dY, const real dZ)
	{
		Matrix4 m_TransformMatrix = GetTranslationMatrix(dX, dY, dZ);
		this->stk_TransformQueue.push(m_TransformMatrix);
	}

	void QueueRotate(const real angleX, const real angleY, const real angleZ)
	{
		Matrix4 m_TransformMatrix = GetRotationMatrix(angleX, angleY, angleZ);
		this->stk_TransformQueue.push(m_TransformMatrix);
	}

	void QueueShear(const real shearX, real shearY, const real shearZ, const real shearXY)
	{
		Matrix4 m_TransformationMatrix = GetShearingMatrix(shearX, shearY, shearZ, shearXY);
		this->stk_TransformQueue.push(m_TransformationMatrix);
	}

	void QueueReflection(const Vector4& direction)
	{
		Matrix4 m_TransformationMatrix = GetReflectionMatrix(direction);
		this->stk_TransformQueue.push(m_TransformationMatrix);
	}

	Vector4 Perform(Vector4& vector)
	{
		real i = vector.i;
		real j = vector.j;
		real k = vector.k;

		Translate(vector, -i, -j, -k);

		Matrix4 m_TransformMatrix;
		while (!this->stk_TransformQueue.empty())
		{
			m_TransformMatrix *= this->stk_TransformQueue.top();
			this->stk_TransformQueue.pop();
		}

		vector = m_TransformMatrix * vector;
		Translate(vector, i, j, k);
		return vector;
	}

	void Reset()
	{
		while (!this->stk_TransformQueue.empty())
			this->stk_TransformQueue.pop();
	}
};

